package org.obm.mina;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class BlobBuilder {

	private static final Logger logger = LoggerFactory
			.getLogger(BlobBuilder.class);

	protected Blob blob;
	private OutputStream out;
	private FileChannel fc;
	private long totalBytes;
	private boolean finished;

	protected BlobBuilder(Blob targetBlob) {
		this.blob = targetBlob;
	}

	public BlobBuilder init() throws IOException, ServiceException {
		FileOutputStream fos = new FileOutputStream(blob.getFile());
		fc = fos.getChannel();
		out = fos;

		return this;
	}

	public BlobBuilder append(byte[] b) throws IOException {
		return append(b, 0, b.length);
	}

	public BlobBuilder append(byte[] b, int off, int len) throws IOException {
		if (finished)
			throw new IllegalStateException("BlobBuilder is finished");

		checkInitialized();

		try {
			out.write(b, off, len);
			totalBytes += len;
		} catch (IOException e) {
			dispose();
			throw e;
		}

		return this;
	}

	public BlobBuilder append(ByteBuffer bb) throws IOException {
		if (!bb.hasArray()) {
			throw new IllegalArgumentException(
					"ByteBuffer must have backing array");
		}
		append(bb.array(), bb.arrayOffset() + bb.position(), bb.remaining());
		bb.position(bb.limit());
		return this;
	}

	private void checkInitialized() throws IOException {
		if (out == null) {
			try {
				init();
			} catch (Exception e) {
				throw (IOException) new IOException(
						"Unable to initialize BlobBuilder").initCause(e);
			}
		}
	}

	public Blob finish() throws IOException, ServiceException {
		if (finished)
			return blob;

		try {
			out.flush();
			fc.force(true);
		} catch (IOException e) {
			dispose();
			throw e;
		} finally {
			ByteUtil.closeStream(out);
		}

		if (logger.isDebugEnabled())
			logger.debug("stored " + this);

		finished = true;
		return blob;
	}

	@Override
	public String toString() {
		File file = blob.getFile();

		return file.getAbsolutePath() + ": data size=" + totalBytes
				+ ", file size=" + file.length();
	}

	public Blob getBlob() {
		if (!finished)
			throw new IllegalStateException("Blob builder not finished");
		return blob;
	}

	public boolean isFinished() {
		return finished;
	}

	// Clean up and dispose of blob file
	public void dispose() {
		if (blob != null) {
			finished = true;
			ByteUtil.closeStream(out);
			StoreManager.getInstance().quietDelete(blob);
			blob = null;
		}
	}
}
